接下來,我們會先寫出Profile的上半部分。
我們使用Pexels圖庫,這是一個CC0圖庫,不需要擔心侵權的問題。
來到ProfileUserDetails.jsx,開始編寫:
import React from "react";
import { TbCircleDashed } from "react-icons/tb";
export const ProfileUserDetails = () => {
return (
<div className="py-10 w-full">
<div className="flex items-center">
<div className="w-[15%]">
<img
className="w-32 h-32 rounded-full"
src="https://images.pexels.com/photos/1128121/pexels-photo-1128121.jpeg?auto=compress"
alt=""
/>
</div>
<div>
<div className="flex space-x-10 items-center">
<p>username</p>
<button>Edit Profile</button>
<TbCircleDashed></TbCircleDashed>
</div>
</div>
</div>
</div>
);
};
export default ProfileUserDetails;
py-10:是設定上下padding的距離。
rounded-full:讓邊邊是圓的。
space-x-10:設定左右的margin。
其餘的在之前的章節有遇到過或是遇到類似的,就不再介紹了。
修改Profile.jsx,顯示剛寫好的ProfileUserDetails。
import React from "react";
import ProfileUserDetails from "../../Components/ProfileComponents/ProfileUserDetails";
const Profile = () => {
return (
<div className="px-20">
<div>
<ProfileUserDetails />
</div>
</div>
);
};
export default Profile;
啟動專案,看到以下的畫面是正常的,因為父元素沒有足夠的寬度來顯示完整的圓。
來到Router.jsx,修改在前的div寬度。
<div className="w-full">
<Routes>
我們就能見到完美的圓了。
繼續完善ProfileUserDetails.jsx,添加一些內容。
<div className="space-y-5">
<div className="flex space-x-10 items-center">
<p>username</p>
<button>Edit Profile</button>
<TbCircleDashed></TbCircleDashed>
</div>
<div className="flex space-x-10">
<div>
<span className="font-semibold mr-2">19</span>
<span>posts</span>
</div>
<div>
<span className="font-semibold mr-2">19</span>
<span>followers</span>
</div>
<div>
<span className="font-semibold mr-2">810</span>
<span>following</span>
</div>
</div>
</div>
space-y-5:設定上下的margin。
啟動專案後,看到以下的畫面,代表成功添加內容。
接下來完成上半區塊,完整的程式碼如下:
import React from "react";
import { TbCircleDashed } from "react-icons/tb";
export const ProfileUserDetails = () => {
return (
<div className="py-10 w-full">
<div className="flex items-center">
<div className="w-[15%]">
<img
className="w-32 h-32 rounded-full"
src="https://images.pexels.com/photos/1128121/pexels-photo-1128121.jpeg?auto=compress"
alt=""
/>
</div>
<div className="space-y-5">
<div className="flex space-x-10 items-center">
<p>username</p>
<button>Edit Profile</button>
<TbCircleDashed></TbCircleDashed>
</div>
<div className="flex space-x-10">
<div>
<span className="font-semibold mr-2">19</span>
<span>posts</span>
</div>
<div>
<span className="font-semibold mr-2">19</span>
<span>followers</span>
</div>
<div>
<span className="font-semibold mr-2">810</span>
<span>following</span>
</div>
</div>
<div>
<p className="font-semibold">Full Name</p>
<p className="font-thin text-sm">
🔥 Living my best life with no regrets 🔥
</p>
</div>
</div>
</div>
</div>
);
};
export default ProfileUserDetails;
接下來,我們要做出選擇Posts、Reels、Saved、Tagged的地方。
在ProfileComponents下,新增ProfileUserPostBar.jsx。
import React, { useState } from "react";
import { AiOutlineTable, AiOutlineUser } from "react-icons/ai";
import { RiVideoAddLine } from "react-icons/ri";
import { BiBookmark } from "react-icons/bi";
const ProfileUserPostBar = () => {
const [activeTab, setActiveTab] = useState([]);
const tabs = [
{
tab: "Post",
icon: <AiOutlineTable></AiOutlineTable>,
activeTab: "",
},
{
tab: "Reels",
icon: <RiVideoAddLine></RiVideoAddLine>,
},
{
tab: "Saved",
icon: <BiBookmark></BiBookmark>,
},
{
tab: "Tagged",
icon: <AiOutlineUser></AiOutlineUser>,
},
];
return (
<div className="flex space-x-14 border-t relative">
{tabs.map((item) => (
<div
onClick={() => setActiveTab(item.tab)}
className={`${
activeTab === item.tab ? "border-t border-black" : "opacity-60"
} flex items-center cursor-pointer py-2 text-sm`}
>
<p>{item.icon}</p>
<p className="ml-1">{item.tab}</p>
</div>
))}
</div>
);
};
export default ProfileUserPostBar;
border-t:代表只有上方才有邊界線。
relative:會根據flex的位置而有所變化。
border-black:邊界線設定成黑色。
opacity-60:設定透明度為60。另外,完全透明為0,不透明是100。
來到Profile.jsx,顯示ProfileUserPostBar的內容。
import React from "react";
import ProfileUserDetails from "../../Components/ProfileComponents/ProfileUserDetails";
import ProfileUserPostBar from "../../Components/ProfileComponents/ProfileUserPostBar";
const Profile = () => {
return (
<div className="px-20">
<div>
<ProfileUserDetails />
</div>
<div>
<ProfileUserPostBar />
</div>
</div>
);
};
export default Profile;
嘗試按下Posts、Reels、Saved、Tagged並觀察它們的變化。
接下來寫Post的部分。
在ProfileComponents下,新增ProfileUserPostCard.jsx,用來呈現Posts底下的一個Post的內容縮圖以及Like和留言數。
import React from "react";
import { AiFillHeart } from "react-icons/ai";
import { FaComment} from "react-icons/fa";
const ProfileUserPostCard = () => {
return (
<div>
<div className="w-60 h-60">
<img className="cursor-pointer" src="https://images.pexels.com/photos/18133619/pexels-photo-18133619.png" alt="" />
<div>
<div>
<div>
<AiFillHeart></AiFillHeart>
<span>10</span>
</div>
<div>
<FaComment />
<span>30</span>
</div>
</div>
</div>
</div>
</div>
);
};
export default ProfileUserPostCard;
回到ProfileUserPostBar.jsx,顯示貼文的效果。
先引入ProfileUserPostCard
import ProfileUserPostCard from "./ProfileUserPostCard";
再來去修改return區塊的內容:
<div>
<div className="flex space-x-14 border-t relative">
{tabs.map((item) => (
<div
onClick={() => setActiveTab(item.tab)}
className={`${
activeTab === item.tab ? "border-t border-black" : "opacity-60"
} flex items-center cursor-pointer py-2 text-sm`}
>
<p>{item.icon}</p>
<p className="ml-1">{item.tab}</p>
</div>
))}
</div>
<div>
<div className="flex flex-wrap">
{[1, 1, 1, 1, 1].map((item) => <ProfileUserPostCard />)}
</div>
</div>
</div>
flex-wrap:允許多行顯示。
在一般情況下看不出變化的人,可以嘗試放大及縮小網頁,就能看到部分內容會到第二第三行。
沒有縮放的情況下,網頁是這樣的。
接著,我們會將Like數、留言數在正常情況下隱藏起來,當滑鼠放在圖片上面時才會顯示。
在ProfileComponents下,新增ProfileUserPostCard.css,這只用在ProfileUserPostCard.jsx上。
.post{
position: relative;
overflow: hidden;
}
.post img{
display: block;
width: 100%;
height: 100%;
}
.post:hover .overlay{
opacity: 1;
}
.overlay{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: opacity .2s ease-in-out;
}
.overlay-text{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: medium;
text-align: center;
width: 30%;
}
overflow: hidden代表元素超出範圍時會直接消失。
display: block代表元素會以矩形呈現。
background-color: rgba(0, 0, 0, 0.5)代表背景是半透明的黑色。
transition: opacity .2s ease-in-out:產生轉場動畫,當opacity發生變化時才會有動作,持續0.2秒,採用先快後慢的變化。
transform: translate(-50%, -50%):把元素沿著 x 軸向左移動 50% 的自身寬度,沿著 y 軸向上移動 50% 的自身高度,就能將元素置中。
回到ProfileUserPostCard.jsx,引用css,修改部分代碼:
import React from "react";
import { AiFillHeart } from "react-icons/ai";
import { FaComment} from "react-icons/fa";
import './ProfileUserPostCard.css';
const ProfileUserPostCard = () => {
return (
<div className="p-2">
<div className="post w-60 h-60">
<img className="cursor-pointer" src="https://images.pexels.com/photos/18133619/pexels-photo-18133619.png" alt="" />
<div className="overlay">
<div className="overlay-text flex justify-between">
<div>
<AiFillHeart></AiFillHeart>
<span>10</span>
</div>
<div>
<FaComment />
<span>30</span>
</div>
</div>
</div>
</div>
</div>
);
};
export default ProfileUserPostCard;
把滑鼠移動到元素上就能看到我們改動的作用了。